package com.kssoft.ebd.spider.pool;

import com.kssoft.ebd.spider.common.config.SpiderConfig;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

/***
 * 频繁的创建销毁driver不合理浪费资源，建立diverpool进行管理。一个spider对应一个pool池
 * @邓明亮
 */
public class WebDriverPool {




    @Autowired
    private SpiderConfig spiderConfig;


    private Logger logger=  LoggerFactory.getLogger(WebDriverPool.class);



    /***
     * 资源池锁
     */
    private Object lockWebDriver = new Object();

//    private final static int ACTIVE_CAPACITY = 25;

    private final static int ACTIVE_CAPACITY = 2;
    private final static int FULL_CAPACITY = 200;
    /***
     * 池化大小
     */

    private final static int STAT_RUNNING = 1;

    private final static int STAT_CLODED = 2;

    private AtomicInteger stat = new AtomicInteger(STAT_RUNNING);

    private WebDriver mDriver = null;


    private static final String DRIVER_FIREFOX = "firefox";

    private static final String DRIVER_CHROME = "chrome";

    private static final String DRIVER_PHANTOMJS = "phantomjs";

    protected static DesiredCapabilities sCaps;

    public WebDriverPool() {
    }



    /**
     * store webDrivers available
     */
    private BlockingDeque<WebDriver> innerQueue = new LinkedBlockingDeque<WebDriver>();


    /**
     * store webDrivers created
     */
    private List<WebDriver> webDriverList = Collections
            .synchronizedList(new ArrayList<WebDriver>());

    /**
     * Configure the GhostDriver, and initialize a WebDriver instance. This part
     * of code comes from GhostDriver.
     * https://github.com/detro/ghostdriver/tree/master/test/java/src/test/java/ghostdriver
     *
     * @throws IOException
     */
    public void configure() throws IOException {
        // Read config file
        //读取文件位置

        // Prepare capabilities
        sCaps = new DesiredCapabilities();
        sCaps.setJavascriptEnabled(true);
        sCaps.setCapability("takesScreenshot", false);

        // Fetch PhantomJS-specific configuration parameters
//        if (driver.equals(DRIVER_PHANTOMJS)) {
//            // "phantomjs_exec_path"
//            if (sConfig.getProperty("phantomjs_exec_path") != null) {
//                sCaps.setCapability(
//                        PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
//                        sConfig.getProperty("phantomjs_exec_path"));
//            } else {
//                throw new IOException(
//                        String.format(
//                                "Property '%s' not set!",
//                                PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY));
//            }
//            // "phantomjs_driver_path"
//            if (sConfig.getProperty("phantomjs_driver_path") != null) {
//               logger.info("Test will use an external GhostDriver");
//                sCaps.setCapability(
//                        PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_PATH_PROPERTY,
//                        sConfig.getProperty("phantomjs_driver_path"));
//            } else {
//                logger.info("Test will use PhantomJS internal GhostDriver");
//            }
//        }

        // Disable "web-security", enable all possible "ssl-protocols" and
        // "ignore-ssl-errors" for PhantomJSDriver
        // sCaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, new
        // String[] {
        // "--web-security=false",
        // "--ssl-protocol=any",
        // "--ignore-ssl-errors=true"
        // });

        ArrayList<String> cliArgsCap = new ArrayList<String>();
        cliArgsCap.add("--web-security=false");
        cliArgsCap.add("--ssl-protocol=any");
        cliArgsCap.add("--ignore-ssl-errors=true");
        sCaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS,
                cliArgsCap);

        // Control LogLevel for GhostDriver, via CLI arguments
        sCaps.setCapability(
                PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_CLI_ARGS,
                new String[] { "--logLevel=INFO"});

        // String driver = sConfig.getProperty("driver", DRIVER_PHANTOMJS);

        // Start appropriate Driver
        String driver = spiderConfig.getDriver();
        if (isUrl(driver)) {
            sCaps.setBrowserName("phantomjs");
            mDriver = new RemoteWebDriver(new URL(driver), sCaps);
        } else if (driver.equals(DRIVER_FIREFOX)) {
            mDriver = new FirefoxDriver(sCaps);
        } else if (driver.equals(DRIVER_CHROME)) {
            ChromeOptions options = new ChromeOptions();
            // 谷歌文档提到需要加上这个属性来规避bug
//            options.addArguments("headless");  //以无头模式运行浏览器，可能有助于隐藏弹窗
            options.addArguments("disable-gpu"); //禁用 GPU 加速，可能解决某些渲染问题。
            options.addArguments("disable-dev-shm-usage");
            options.addArguments("disable-plugins");
            // 禁用java
            options.addArguments("disable-java");
            // 以最高权限运行
            options.addArguments("no-sandbox");
            options.addArguments("user-agent=\"Mozilla/5.0 (iPod; U; CPU iPhone OS 2_1 like Mac OS X; ja-jp) AppleWebKit/525.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile/5F137 Safari/525.20\"");
            //不显示弹出窗口
            options.setHeadless(spiderConfig.isHeadless());
            options.setAcceptInsecureCerts(true); // 自动接受不安全证书
            options.addArguments("--ignore-certificate-errors"); // 忽略所有证书错误
            options.setBinary(spiderConfig.getChromePath());
            mDriver = new ChromeDriver(options);
            logger.info("创建的对象是此对象！");
        } else if (driver.equals(DRIVER_PHANTOMJS)) {
            mDriver = new PhantomJSDriver(sCaps);
        }
    }


    /**
     * check whether input is a valid URL
     *
     * @param urlString urlString
     * @return true means yes, otherwise no.
     */
    private boolean isUrl(String urlString) {
        try {
            new URL(urlString);
            return true;
        } catch (MalformedURLException mue) {
            return false;
        }
    }


    /**
     *
     * @return 生成浏览器管理对象，对象受制于配置的管理池大小，即用即还。
     * @throws InterruptedException
     */
    public  WebDriver getSigle(){
        synchronized(lockWebDriver){
            try{
                WebDriver poll = null ;
                poll = innerQueue.poll();
                if (poll != null) {
                    try {
                        poll.manage().window().getSize();
                        return poll;
                    }catch (Exception e){
                        logger.error("操作对象获取异常",e);
                        webDriverList.remove(poll);
                        poll.quit();
                        poll=null;
                    }
                }
                if (webDriverList.size() < FULL_CAPACITY) {
                    try {
                        configure();
                        innerQueue.add(mDriver);
                        webDriverList.add(mDriver);
                        return innerQueue.poll();
                    } catch (Exception e) {
                        logger.error("创建句柄异常：",e);
                        return  null;
                    }
                }else{
                    logger.info("浏览器对象缓存已满，不需要再进行创建和管理");
                    return null;
                }
            }catch (Exception e){
                logger.error("外部创建句柄异常：",e);
                return  null;
            }
        }
    }


    /***
     * 初始化资源池,活性资源数量等于定义数
     */
    public void initWebDriver(){
        for(int i=0;i<ACTIVE_CAPACITY;i++){
            try {
                configure();
                innerQueue.add(mDriver);
                webDriverList.add(mDriver);

            } catch (Exception e) {
                logger.error("初始化资源池异常：",e);
            }
            logger.info("当前初始化完成后的资源池："+innerQueue.size()+"资源池的大小："+webDriverList.size());
        }
    }


    /***
     * 回收资源池
     * @param webDriver
     */
    public void returnToPool(WebDriver webDriver) {
        try{
            if(webDriver==null){
                return;
            }
            checkRunning();
            webDriver.manage().deleteAllCookies();
        }catch (Exception e){
            logger.error("返回资源池异常",e);
        }
        innerQueue.add(webDriver);
        //返回资源池做核心活性数量检查，超过活性数量则退出停止
        synchronized(lockWebDriver){
            while (innerQueue.size()>ACTIVE_CAPACITY){
                logger.info("****销毁前"+innerQueue.size()+"队列的总量："+webDriverList.size());
                WebDriver driver = innerQueue.poll();
                webDriverList.remove(driver);
                driver.quit();
                driver=null;
                logger.info("****销毁后："+innerQueue.size()+"队列的总量："+webDriverList.size());
            }
//            logger.info("****当前队列核心数量："+innerQueue.size()+"队列的总量："+webDriverList.size());
        }
    }


    /***
     * 利用原子性检测
     */
    protected void checkRunning() {
        if (!stat.compareAndSet(STAT_RUNNING, STAT_RUNNING)) {
            throw new IllegalStateException("Already closed!");
        }
    }



    public void closeAll() {
        boolean b = stat.compareAndSet(STAT_RUNNING, STAT_CLODED);
        if (!b) {
            throw new IllegalStateException("Already closed!");
        }
        for (WebDriver webDriver : webDriverList) {
            logger.info("Quit webDriver" + webDriver);
            try{
                webDriver.quit();
            }catch (Exception e){
                logger.error("退出异常",e);
            }
            webDriver = null;
        }
        webDriverList.clear();
        innerQueue.clear();
        logger.info("innerQueue的大小" + innerQueue.size());
    }



    public int webDriverLenth(){
        return webDriverList.size();
    }

    public int innerQueueLenth(){
        return innerQueue.size();
    }
}
